<html>
<head>
<title>CpcPaChi - Version 0.10</title>
<style type="text/css">
span {
	font-family: 'Courier New';
	font-size: 10pt;
	color: #000000;
}
.sc0 {
	color: #808080;
}
.sc3 {
	color: #008080;
}
.sc4 {
	color: #FF8000;
}
.sc5 {
	font-weight: bold;
	color: #0000FF;
}
.sc7 {
	color: #808080;
}
.sc9 {
	color: #804000;
}
.sc10 {
	font-weight: bold;
	color: #000080;
}
.sc11 {
}
</style>
</head>
<body>
<h2>Un compilador sencillo paso a paso (10 - Variables &quot;utilizables&quot;)</h2>

<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>

<p>En el acercamiento anterior hab&iacute;amos comentado alguna de las ideas b&aacute;sicas del manejo de variables, y hab&iacute;amos un primer acercamiento al analizador que reconozca la declaraci&oacute;n de variables, pero todav&iacute;a no sab&iacute;amos manipularlas. Eso es lo que haremos ahora.</p>
<p>Tendremos que ser capaces de hacer cosas como: </p>
<ul>
  <li>Dar un valor a una variable: a := 1 </li>
  <li>Cambiar el valor de una variable. La primera forma que haremos, porque no supone analizar expresiones aritm&eacute;ticas, podr&iacute;a ser la operaci&oacute;n &quot;incremento&quot;: inc(a). </li>
  <li>Usar el valor de una variable, con operaciones como cpcMode(a).</li>
</ul>
<p>Como las variables se almacenan en operaciones de memoria, todas esas operaciones supondr&aacute;n leer el valor de la memoria, y, a veces, modificarlo y volverlo a guardar. En concreto, si suponemos que la variable &quot;a&quot; est&aacute; en la posici&oacute;n de memoria 10.000, esas operaciones se podr&iacute;an convertir a las siguientes secuencias en ensamblador:</p>
<p><b>a := 1</b></p>
<pre><code>ld a, 1 ; Cargar el valor 1 en el acumulador<br />
ld (10000), a ; Guardar en la posici&oacute;n 10.000 el valor del acumulador </code></pre>
<p>&nbsp;</p>
<p><b>inc(a)</b></p>
<pre><code>ld a, (10000) ; Guardar en el acumulador el valor guardado en la posici&oacute;n 10.000<br />
inc a ; Incrementar el valor del acumulador<br />
ld (10000), a ; Guardar en la posici&oacute;n 10.000 el valor del acumulador 
</code></pre>
<p>&nbsp;</p>
<p><b>cpcMode(a)</b></p>
<pre><code>ld a, (10000) ; Guardar en el acumulador el valor guardado en la posici&oacute;n 10.000<br />
call &amp;bc0e ; Cambiar modo de pantalla</code></pre>
<p>&nbsp;</p>
<p>Esto supone varios cambios en el fuente: para instrucciones como INC bastar&aacute; con a&ntilde;adir nuevas rutinas de generaci&oacute;n de c&oacute;digo, pero para &oacute;rdenes existentes como cpcMode ahora deberemos comprobar si el par&aacute;metro es una constante (y en ese caso har&iacute;amos cosas como LD A,1) o si es una variable (y entonces tomaremos el valor desde una posici&oacute;n de memoria: LD A, (10000) ). </p>
<p>Pero tambi&eacute;n hay otro cambio grande: &iquest;c&oacute;mo asignamos las direcciones de memoria para esas variables?&nbsp; &iquest;Y d&oacute;nde <b>dejamos el espacio</b> para ellas?</p>
<p>Hay varias soluciones:</p>
<ul>
  <li>La primera soluci&oacute;n, la m&aacute;s eficiente en cuanto a espacio, ser&iacute;a dejar el espacio para las variables al final del programa. Pero esta soluci&oacute;n tiene un problema: hasta que no terminamos de generar el programa, no sabemos cu&aacute;nto ocupa, de modo que no hasta entonces no sabr&iacute;amos las direcciones de las variables, por lo que deber&iacute;amos dar dos pasadas: en la primera pasada se genera todo el programa excepto las direcciones de las variables (se suele dejar indicado con una etiqueta, algo como &quot;LD A, [variableX]&quot;), y en la segunda pasada ser&aacute; cuando se escriban realmente las posiciones de memoria en lugar de estas etiquetas.</li>
  <li>La segunda soluci&oacute;n es dejar el espacio para variables al principio del programa. Ahora el inconveniente ser&aacute; parecido: como no sabemos cuantas variables hay en el programa, no tendremos claro en qu&eacute; posici&oacute;n de memoria empezar&aacute;n las &oacute;rdenes... hasta que no demos una primera pasada. Adem&aacute;s, existe un segundo riesgo: antes lanz&aacute;bamos nuestro programa resultante con algo como CALL 40000, pero ahora la primera orden ya no est&aacute; en esa posici&oacute;n, sino en otra, as&iacute; que deberemos recordar en cual, para que el cargador sea adecuado (por ejemplo, CALL 40015).</li>
  <li>Nosotros queremos esquivar (todav&iacute;a) las dos pasadas, e intentar hacerlo todo en una pasada. As&iacute; que la aproximaci&oacute;n que haremos es dar por sentado que &quot;algo&quot; ocupa una cierta cantidad de bytes como m&aacute;ximo. Por ejemplo, podr&iacute;amos suponer que el programa empieza en la posici&oacute;n de memoria 30.000, y que no ocupa m&aacute;s de 10.000 bytes, por lo que las variables podr&iacute;an estar en la posici&oacute;n de memoria 40.000. Como el tama&ntilde;o de un programa es algo muy dif&iacute;cil de prever, y la cantidad de variables no lo es tanto, quiz&aacute; fuera m&aacute;s razonable decir que las variables empiezan en la posici&oacute;n 30.000 (por ejemplo) y el programa en la 30.200. No es una aproximaci&oacute;n fiable, pero s&iacute; es sencilla, y todav&iacute;a buscamos sobre todo sencillez. Para evitar &quot;despistes&quot; en los que un CALL 30000 no accediera al programa sino al comienzo de los datos, lo que podr&iacute;a tener resultados imprevisibles, nos reservaremos los primeros 3 bytes de datos, no para variables, sino para almacenar una orden de salto a la posici&oacute;n 30200 (o a la que hayamos decidido que ser&aacute; la de comienzo del programa), o bien podemos obligarnos a rellenar con 0 (NOP) toda esa zona de variables inicialmente. </li>
</ul>
<p>Vamos a ver los cambios que todo esto supondr&iacute;a al c&oacute;digo del compilador:</p>
<p>En primer lugar, en la tabla de s&iacute;mbolos podremos guardar constantes y variables, luego podemos a&ntilde;adir un dato &quot;constVar&quot; que sea el que indique si un s&iacute;mbolo es una constante (cuyo valor podremos leer directamente en la tabla, pero no modificar) o una variable (para la que guardaremos la direcci&oacute;n de memoria, y el valor obtenido se deber&aacute; poder modificar). Por tanto, el registro de cada s&iacute;mbolo podr&iacute;a ser:</p>
<pre><code>
  type simbolo =
    record
      nombre: string;    (* Nombre del simbolo *)
      constVar: integer; (* Si es CONST o VAR *)
      tipoDato: integer; (* Tipo base: BYTE, etc *)
      valorDir: string;  (* Valor o direccion *)
    end;
</code></pre>
<p>Para ese dato &quot;constVar&quot; que indica si es una constante o una variable, podemos definir dos constantes que nos ayuden a mantener la legibilidad del c&oacute;digo:</p>
<pre><code>
  const
    tCONST = 1; (* Dato CONST en tabla de simbolos *)
    tVAR = 2;   (* Dato VAR en tabla de simbolos *)
</code></pre>
<p>Ahora la rutina de insertar una constante deber&aacute; tener ese dato en cuenta:</p>
<pre><code>
  (* Guardar una constante en la tabla de simbolos *)
  procedure insertarSimboloConst(nombreConst: string; valorConst: string);
  var
    i: integer;
  begin
    nombreConst := upcase(nombreConst);
    (* Primero comprobamos si el simbolo ya existe *)
    for i:=1 to numSimbolos do
      if listaSimbolos[i].nombre = nombreConst then
          errorFatal('Constante repetida!');
    (* Si no existe, lo insertamos *)
    numSimbolos := numSimbolos + 1;
    listaSimbolos[numSimbolos].nombre := nombreConst;
    listaSimbolos[numSimbolos].constVar := tCONST;
    listaSimbolos[numSimbolos].tipoDato := tBYTE;
    listaSimbolos[numSimbolos].valorDir := valorConst;
  end;
</code></pre>
<p>La rutina de insertar una variable ser&aacute; parecida, salvo porque no tenemos un valor, sino una direcci&oacute;n. Adem&aacute;s, esta direcci&oacute;n no la deber&iacute;amos fijar nosotros a mano, sino que se deber&iacute;a ir recalculando a medida que guardemos variables. As&iacute;, podr&iacute;amos delegar en una funci&oacute;n auxiliar &quot;proximaDireccionLibre&quot;, a la que adem&aacute;s le podemos indicar el tama&ntilde;o del dato, de forma que sea algo m&aacute;s eficiente cuando nuestras variables no sean s&oacute;lo de tipo byte:</p>
<pre><code>
  (* Guardar una variable en la tabla de simbolos *)
  procedure insertarSimboloVar(nombreVar: string; tipoVar: char);
  var
    i: integer;
  begin
    nombreVar := upcase(nombreVar);
    (* Primero comprobamos si el simbolo ya existe *)
    for i:=1 to numSimbolos do
      if listaSimbolos[i].nombre = nombreVar then
        errorFatal('Identificador repetido!');
    (* Si no existe, lo insertamos *)
    numSimbolos := numSimbolos + 1;
    listaSimbolos[numSimbolos].nombre := nombreVar;
    if tipoVar = 'b' then
      listaSimbolos[numSimbolos].tipoDato := tBYTE
    else
      begin
        writeln('Tipo de datos desconocido!');
        halt;
      end;
    listaSimbolos[numSimbolos].constVar := tVAR;    
    (* Direccion, en vez de valor *)
    listaSimbolos[numSimbolos].valorDir := proximaDireccionLibre(tByte);
  end;
</code></pre>

<p>Finalmente, ahora las funciones podr&aacute;n tener como par&aacute;metros constantes
num&eacute;ricas, constantes con valor, o variables, por lo que su an&aacute;lisis &quot;previo&quot; ser&aacute; algo m&aacute;s trabajoso que antes, as&iacute; que podr&iacute;amos crear una rutina espec&iacute;fica que se encargue de ello. De este modo, rutinas como la de analizar la orden PEN, que antes era as&iacute;:</p>

<pre><code>
procedure analizarPEN;
begin
  leerSimbolo('(');

  cadenaTemp := obtenerEnteroOIdentificador;
  if cadenaTemp[1] in ['a'..'z'] then
    x := leerSimboloConst(cadenaTemp)
  else
    val(cadenaTemp,x,codError);

  leerSimbolo(')');
  leerSimbolo(';');
  genPEN(x);
end;
</code></pre>

<p>Pasar&aacute;n a ser simplemente as&iacute;:</p>

<pre><code>
procedure analizarPEN;
begin
  leerSimbolo('(');
  genLecturaValor(obtenerEnteroOIdentificador);
  leerSimbolo(')');
  leerSimbolo(';');
  genPEN(x);
end;
</code></pre>

<p>Y ese procedimiento encargado de generar la orden de lectura
del valor seg&uacute;n se trate de una constante num&eacute;rica, una constante
con nombre o una variable podr&iacute;a ser as&iacute;:</p>

<pre><code>
procedure genLecturaValor(nombre: string);
var 
  valorByte: byte;
  direccion: integer;
begin
  if nombre[1] in ['0'..'9'] then
    begin
    (* Si es numero *)
    val(nombre,valorByte,codError);
    writeln( ficheroDestino, lineaActual,' DATA 3E,',
       hexStr(valorByte,2), ': '' LD A, ',valorByte );
    lineaActual := lineaActual + 10;
    longitudTotal := longitudTotal + 2;    
    end
  else
    if leerSimboloTS(nombre) = tCONST then
      (* Si es constante con nombre *)
      begin
      valorByte := leerSimboloConst(nombre);
      writeln( ficheroDestino, lineaActual,' DATA 3E,',
         hexStr(valorByte,2), ': '' LD A, ',valorByte, ' - Constante ',nombre );
      lineaActual := lineaActual + 10;
      longitudTotal := longitudTotal + 2;          
      end
    else
      (* Si es variable *)
      begin
      direccion := leerSimboloVar(nombre);
      writeln( ficheroDestino, lineaActual,' DATA 3A,',
        hexStr(direccion mod 256,2), ',',
        hexStr(direccion div 256,2),
        ': '' LD A, (',direccion, ') - Variable ',nombre );
      lineaActual := lineaActual + 10;
      longitudTotal := longitudTotal + 3;          
      end;    
end;
</code></pre>

<p><br>
  Como siempre, para m&aacute;s detalles, todo el c&oacute;digo est&aacute; en la p&aacute;gina del proyecto en Google Code:<br />
  <br />
  <a href="http://code.google.com/p/cpcpachi/">http://code.google.com/p/cpcpachi/</a></p>
<p align="right">(Volver al <a href="doc.html">&iacute;ndice</a>) </p>
</body>
</html>
